package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestQuasiquoteBasic(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{"`1", "1"},
		{"`#t", "#t"},
		{"`hello", "hello"},
		{"`(1 2 3)", "(1 2 3)"},
		{"`((1 2) 3)", "((1 2) 3)"},
		{"\"hello\"", "\"hello\""},
		{"`\"hello\"", "\"hello\""},
		{"`(1 . 2)", "(1 . 2)"},
		{"`(+ 1 2)", "(+ 1 2)"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		obj, err := EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("Eval() error = %v", err)
		}
		if obj.Inspect() != tt.expected {
			t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.expected)
		}
	}
}

func TestQuasiquoteWithUnquote(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{"(define x 5) `(1 2 ,x 3)", "(1 2 5 3)"},
		{"(define y '(a b)) `(1 ,y 2)", "(1 (a b) 2)"},
		{"(define z 10) `(list ,z ,(+ z 1) ,(+ z 2))", "(list 10 11 12)"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Fatalf("parser.ParseProgram() error = %v", err)
		}

		var result obj.Object
		for _, expr := range exprs {
			result, err = EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("Eval() error = %v", err)
			}
		}

		if result.Inspect() != tt.expected {
			t.Errorf("Eval() = %v, want %v", result.Inspect(), tt.expected)
		}
	}
}

func TestQuasiquoteWithUnquoteSplicing(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{"(define x '(2 3)) `(1 ,@x 4)", "(1 2 3 4)"},
		{"(define y '((a b) (c d))) `(1 ,@y 2)", "(1 (a b) (c d) 2)"},
		{"(define z '()) `(1 ,@z 2)", "(1 2)"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Fatalf("parser.ParseProgram() error = %v", err)
		}

		var result obj.Object
		for _, expr := range exprs {
			result, err = EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("Eval() error = %v", err)
			}
		}

		if result.Inspect() != tt.expected {
			t.Errorf("Eval() = %v, want %v", result.Inspect(), tt.expected)
		}
	}
}

func TestQuasiquoteNested(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{"(define x 5) `(a `(b ,x))", "(a (quasiquote (b (unquote x))))"},
		// {"(define x 5) `(a `(b ,,x))", "(a `(b 5))"}, // This would require double unquote which is not standard
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Fatalf("parser.ParseProgram() error = %v", err)
		}

		var result obj.Object
		for _, expr := range exprs {
			result, err = EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("Eval() error = %v", err)
			}
		}

		if result.Inspect() != tt.expected {
			t.Errorf("Eval() = %v, want %v", result.Inspect(), tt.expected)
		}
	}
}
